<template>
  <!-- 3D动画背景 -->
  <div
    ref="three_box"
    :style="`width:${props.width};height:${props.height}`"
  ></div>
</template>

<script setup>
import * as THREE from "three";
import { onMounted, ref } from "vue";
const props = defineProps({
  width: {
    default: "100%",
  },
  height: {
    default: "100%",
  },
});
const three_box = ref(null);
/**
 * 根据容器大小调整宽高比
 * @param domElement  渲染容器
 * @param camera      摄像机
 * @param renderer    渲染器
 */
const setAspectRatio = (domElement, camera, renderer) => {
  // 监听元素的大小变化
  const resizeObserver = new ResizeObserver(() => {
    // 更新摄像机的宽高比
    camera.aspect = domElement.offsetWidth / domElement.offsetHeight;
    // 更新摄像机的投影矩阵
    camera.updateProjectionMatrix();
    // 更新渲染器
    renderer.setSize(domElement.offsetWidth, domElement.offsetHeight);
    // 设置渲染器的像素比
    renderer.setPixelRatio(domElement.devicePixelRatio);
  });
  // 监听元素大小变化
  resizeObserver.observe(domElement);
};
// 初始化3D场景
const init = () => {
  // 创建一个场景
  const scene = new THREE.Scene();
  // 创建一个相机
  const camera = new THREE.PerspectiveCamera(
    90,
    three_box.value.offsetWidth / three_box.value.offsetHeight,
    0.1,
    1000
  );
  // 如果两边有黑边，是因为没有铺满，请把相机的z轴位置调小一点
  camera.position.z = 3;
  // 创建一个渲染器
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(three_box.value.offsetWidth, three_box.value.offsetHeight);
  three_box.value.appendChild(renderer.domElement);
  new Promise((resolve) => {
    // 图片尺寸
    const img = new Image();
    img.onload = function () {
      //   加载纹理
      const textureLoader = new THREE.TextureLoader();
      const texture = textureLoader.load(img.src);
      const depthTexture = textureLoader.load(
        new URL("./img/bg_depth.png", import.meta.url).href
      );
      // 鼠标位置
      const mouse = new THREE.Vector2();
      // 着色器材质
      const material = new THREE.ShaderMaterial({
        uniforms: {
          uTexture: { value: texture },
          uDepthTexture: { value: depthTexture },
          uMouse: { value: mouse },
          uTime: { value: 0 },
        },
        vertexShader: `
          varying vec2 vUv;
          void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); 
          }
          `,
        fragmentShader: `
          uniform sampler2D uTexture;
          uniform sampler2D uDepthTexture;
          uniform float uTime;
          uniform vec2 uMouse;
          varying vec2 vUv;
          void main() {
            vec4 color = texture2D(uTexture, vUv);
            vec4 depth = texture2D(uDepthTexture, vUv);
            float depthValue = depth.r;
            float x = vUv.x - (uMouse.x+sin(uTime))*0.01*depthValue;
            float y = vUv.y - (uMouse.y+cos(uTime))*0.01*depthValue;
            vec4 newColor = texture2D(uTexture, vec2(x, y));
            gl_FragColor = newColor;
          }
          `,
      });
      // 创建一个平面
      const planeGeometry = new THREE.PlaneGeometry(
        this.width / 100,
        this.height / 100
      );
      const plane = new THREE.Mesh(planeGeometry, material);
      scene.add(plane);
      // 监听鼠标移动事件
      window.addEventListener("mousemove", (e) => {
        const x = (e.clientX / window.innerWidth) * 2 - 1;
        const y = -(e.clientY / window.innerHeight) * 2 + 1;
        material.uniforms.uMouse.value.set(x, y);
      });
      resolve(material);
    };
    img.src = new URL("./img/bg.png", import.meta.url).href;
  }).then((material) => {
    // 渲染
    requestAnimationFrame(function animate() {
      material.uniforms.uTime.value = performance.now() / 1000;
      renderer.render(scene, camera);
      requestAnimationFrame(animate);
    });
    setAspectRatio(three_box.value, camera, renderer);
  });
};
onMounted(() => {
  init();
});
</script>

<style scoped></style>
